package com.pocket.demo.business.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.pocket.demo.base.consts.RequestConst;
import com.pocket.demo.base.consts.SecurityConst;
import com.pocket.demo.base.utils.ServletUtils;
import com.pocket.demo.business.po.Menu;
import com.pocket.demo.business.po.Role;
import com.pocket.demo.business.po.User;
import com.pocket.demo.business.service.MenuService;
import com.pocket.demo.business.service.PocketUserDetailsService;
import com.pocket.demo.business.service.RoleService;
import com.pocket.demo.business.service.UserService;
import com.pocket.demo.core.context.TokenContext;
import com.pocket.demo.core.i18n.MessageSourceService;
import com.pocket.demo.core.security.PocketUser;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 【demo-SpringSecurity-3.3】
 * @author zhaozhile
 */
@Service
public class UserDetailsServiceImpl implements PocketUserDetailsService {

    @Resource
    private UserService userService;

    @Resource
    private RoleService roleService;
    @Resource
    private MenuService menuService;
    @Resource
    private RedisTemplate<String, PocketUser> redisTemplate;

    @Override
    public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException {
        User user = userService.getByLoginNameOrEmailOrMobile(loginId);
        if(Objects.isNull(user)){
            throw new UsernameNotFoundException(loginId);
        }

        // 【demo-SpringSecurity-5.2】
        List<Role> roles = roleService.listByUserId(user.getUserId());
        Set<GrantedAuthority> authorities = new HashSet<>();
        Set<String> permissions = new HashSet<>();
        if (CollectionUtil.isNotEmpty(roles)){
            authorities.addAll(roles.stream().map(item -> new SimpleGrantedAuthority(SecurityConst.getSecurityRole(item.getRoleKey()))).collect(Collectors.toSet()));
            permissions.addAll(roles.stream().map(item -> SecurityConst.getSecurityRole(item.getRoleKey())).collect(Collectors.toSet()));
        }

        // 【demo-SpringSecurity-6.1】
        Set<Long> roleIds = roles.stream().map(Role::getRoleId).collect(Collectors.toSet());
        List<Menu> menus = menuService.listByRoleId(roleIds);
        if (CollectionUtil.isNotEmpty(roles)){
            authorities.addAll(menus.stream().map(item -> new SimpleGrantedAuthority(item.getPerms())).collect(Collectors.toSet()));
            permissions.addAll(menus.stream().map(Menu::getPerms).collect(Collectors.toSet()));
        }

        return new PocketUser(user.getNickName(), user.getLoginPassword(), authorities, permissions);
    }

    @Override
    public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
        // 【demo-SpringSecurity-4.2】
        response.addHeader(RequestConst.HEAD_TOKEN, TokenContext.getToken());
        ServletUtils.renderString(response, JSONUtil.toJsonStr(MessageSourceService.getOkResult()));
    }

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){
        String token = TokenContext.getToken();
        if (StringUtils.isNotBlank(token)){
            redisTemplate.delete(token);
        }
        ServletUtils.renderString(response, JSONUtil.toJsonStr(MessageSourceService.getOkResult()));
    }

    @Override
    public PocketUser loadUserBySessionId(String sessionId) {
        if (StringUtils.isBlank(sessionId)){
            return null;
        }
        return redisTemplate.opsForValue().get(sessionId);
    }

    @Override
    public void saveUserSession(String sessionId, PocketUser user) {
        if (StringUtils.isBlank(sessionId) || Objects.isNull(user)){
            throw new BadCredentialsException("sessionId or user is null");
        }
        redisTemplate.opsForValue().set(sessionId, user);
    }

}
